﻿using Microsoft.WindowsAzure.ServiceRuntime;
using POC.Azure.Model;
using POC.Azure.Storage;
using POC.Configuration;
using POC.Loggers.Model;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Hosting;

namespace POC.Loggers.Listeners
{   
    public abstract  class AzureStorageTraceListener : TraceListener
    {
        #region Variables
        private bool initialized;
        protected  AzureAccount storageAccount;
        protected  AzureAccountSettings settings = new AzureAccountSettings();
        protected bool async;
        protected bool plainTextMessages;
        private const string defaultCategory = "Information";
        #endregion

        #region .ctor
        protected AzureStorageTraceListener(string initializeData)
            : base()
        {
            this.settings = new AzureAccountSettings();
            this.settings.Parse(initializeData);
            if (!string.IsNullOrEmpty(this.settings.filePath))
            {
                ReadConfigSource(this.settings.filePath);
            }
        }
        #endregion
        
        #region Override
        protected virtual void Initialize()
        {
            if (!initialized)
            {
                storageAccount = ConfgureAccount();               
                this.async =  settings.async;
                initialized = true;
            }
        }
        protected override string[] GetSupportedAttributes()
        {
            return ConfigConst.GetSupportedTypes();
        }
        public override void Close()
        {
            this.Flush();
            base.Close();
        }
        #endregion

        #region Write/WriteLine
        public override void Write(string message)
        {
            this.WriteLine(message);
        }

        public override void Write(object o)
        {
            this.WriteLine(Convert.ToString(o));
        }

        public override void Write(object o, string category)
        {
            this.WriteLine(Convert.ToString(o), category);
        }

        public override void Write(string message, string category)
        {
            this.WriteLine(message, category);
        }

        public override void WriteLine(string message)
        {
            this.WriteLine(message, defaultCategory);
        }

        public override void WriteLine(object o)
        {
            this.WriteLine(Convert.ToString(o), defaultCategory);
        }

        public override void WriteLine(object o, string category)
        {
            base.WriteLine(Convert.ToString(o), category);
        }

        public override void WriteLine(string message, string category)
        {
            if (string.IsNullOrEmpty(message))
                return;

            LogEntry logEntry = new LogEntry(message, category);
            logEntry.Category = category;
            this.PutEntry(logEntry);
        }
        #endregion

        #region TraceEvent/TraceData
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
            {
                string message = string.Empty;
                if (data != null)
                {
                    message = data.ToString();
                }
                LogEntry logEntry = new LogEntry(source, eventType, id, eventCache, TraceOutputOptions, message);
                this.PutEntry(logEntry);
            }
        }

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data)
        {
            if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, null, data))
            {
                StringBuilder builder = new StringBuilder();
                if (data != null)
                {
                    for (int i = 0; i < data.Length; i++)
                    {
                        if (i != 0)
                        {
                            builder.Append("| ");
                        }
                        if (data[i] != null)
                        {
                            builder.Append(data[i].ToString());
                        }
                    }
                }
                LogEntry logEntry = new LogEntry(source, eventType, id, eventCache, TraceOutputOptions, builder.ToString());
                this.PutEntry(logEntry);
            }
        }

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id)
        {
            base.TraceEvent(eventCache, source, eventType, id);
        }
        /// <summary>
        /// So far this is cought....
        /// </summary>
        /// <param name="eventCache"></param>
        /// <param name="source"></param>
        /// <param name="eventType"></param>
        /// <param name="id"></param>
        /// <param name="message"></param>
        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
        {
            if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, message, null, null, null))
            {
                LogEntry logEntry = new LogEntry(source, eventType, id, eventCache, TraceOutputOptions, message);
                this.PutEntry(logEntry);
            }
        }

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
        {
            if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, format, args, null, null))
            {
                LogEntry logEntry = new LogEntry(source, eventType, id, eventCache, TraceOutputOptions,
                    args != null ? string.Format(CultureInfo.InvariantCulture, format, args) : format);
                this.PutEntry(logEntry);
            }
        }

        public override void TraceTransfer(TraceEventCache eventCache, string source, int id, string message, Guid relatedActivityId)
        {
            base.TraceTransfer(eventCache, source, id, message, relatedActivityId);
        }
        #endregion

        #region Abstract
        protected abstract void InitializeService();
        protected abstract void PutEntry(LogEntry entry);
        #endregion

        #region Privare Utility
        private void ReadConfigSource(string filePath)
        {
            IConfigReader reader = null;
            if (HostingEnvironment.IsHosted)
            {
                // TODO ..
                string configPath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, this.settings.filePath);
                reader = new FileConfigReader(configPath, this.settings.nodeName);
            }
            else
            {
                reader = new FileConfigReader(this.settings.filePath, this.settings.nodeName);
            }
            var items = reader.List(this.settings.nodeName);
            foreach (var item in items)
            {
                this.settings.Add(item.Key, item.Value);
            }
        }
        private AzureAccount ConfgureAccount()
        {
            //Set cloudAccount .. Read Config...
            if (!string.IsNullOrEmpty(this.settings.connectionStringName))
            {
                storageAccount = new AzureAccount(this.settings.connectionStringName);
            }
            else if (!string.IsNullOrEmpty(this.settings.account) && !string.IsNullOrEmpty(this.settings.accessKey))
            {
                storageAccount = new AzureAccount(this.settings.account, this.settings.accessKey, this.settings.isHttps);
            }
            else
            {
                storageAccount = new AzureAccount(this.settings.useLocalAccount);
            }
            return storageAccount;
        }
        #endregion
    }
}
